{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "02c86170-6b8b-46d2-ac09-a75516d48cfa",
   "metadata": {},
   "source": [
    "# State Preparation\n",
    "\n",
    "Most quantum applications start with preparing a state in a quantum register.\n",
    "For example, in finance the state may represent the price distribution of some assets.\n",
    "In chemistry, it may be an initial guess for the ground state of a molecule, and in\n",
    "a quantum machine learning, a feature vector to analyze.\n",
    "\n",
    "The state preparation functions creates a quantum program that\n",
    "outputs either a probability distribution $p_{i}$ or a real amplitudes\n",
    "vector $a_{i}$ in the computational basis, with $i$ denoting the corresponding\n",
    "basis state. The amplitudes take the form of list of float numbers.\n",
    "The probabilities are a list of positive numbers. This is the resulting wave function for probability:\n",
    "\n",
    "$$\n",
    "\\left|\\psi\\right\\rangle = \\sum_{i}\\sqrt{p_{i}}\n",
    "\\left|i\\right\\rangle,\n",
    "$$\n",
    "\n",
    "and this is for amplitude:\n",
    "\n",
    "$$\n",
    "\\left|\\psi\\right\\rangle = \\sum_{i}a_{i}\n",
    "\\left|i\\right\\rangle.\n",
    "$$\n",
    "\n",
    "In general, state preparation is hard. Only a very small portion\n",
    "of the Hilbert space can be prepared efficiently (in $O(poly(n))$\n",
    "steps) on a quantum program. Therefore, in practice, an approximation\n",
    "is often used to lower the complexity. The approximation is specified\n",
    "by an error bound, using the [$L_2$ norm](https://en.wikipedia.org/wiki/Lp_space).\n",
    "\n",
    "The higher the specified error tolerance, the smaller the output\n",
    "quantum program. For exact state preparation, specify an error bound of $0$.\n",
    "\n",
    "The state preparation algorithm can be tuned depending on whether the\n",
    "probability distribution is sparse or dense. The synthesis engine will\n",
    "automatically select the parameterization based on the given constraints and\n",
    "optimization level."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f8b513d5-4d53-488c-9d8f-b3e98233c2f1",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true
   },
   "source": [
    "Function: `prepare_state`\n",
    "\n",
    "Parameters:\n",
    "\n",
    "- `probabilities: CArray[CReal]` - Probabilities to load. Should be non-negative and sum to 1.\n",
    "- `bound: CReal` - Approximation Error Bound, in the $L_2$ metric (with respect to the given probabilies vector).\n",
    "- `out: Output[QArray[QBit]]`\n",
    "\n",
    "\n",
    "\n",
    "Function: `inplace_prepare_state`\n",
    "\n",
    "Parameters:\n",
    "\n",
    "- `probabilities: CArray[CReal]`\n",
    "- `bound: CReal`\n",
    "- `out: QArray[QBit]` - Should of size exactly $\\log_2$(``probabilities.len`)\n",
    "\n",
    "The `inplace_prepare_state` works the same, but for a given allocated `QArray`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "07470a72-7f9d-42a7-b98e-4acf18edf63a",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true
   },
   "source": [
    "Function: `prepare_amplitudes`\n",
    "\n",
    "Parameters:\n",
    "\n",
    "- `amplitudes: CArray[CReal]` - Amplitudes of the loaded state. Each should be real and the vector norm should be equal to 1.\n",
    "- `bound: CReal` - Approximation Error Bound, in the $L_2$ metric (with respect to the given amplitudes vector).\n",
    "- `out: Output[QArray[QBit]]`\n",
    "\n",
    "\n",
    "\n",
    "Function: `inplace_prepare_amplitudes`\n",
    "\n",
    "Parameters:\n",
    "\n",
    "- `amplitudes: CArray[CReal]` - Amplitudes of the loaded state. Each should be real and the vector norm should be equal to 1.\n",
    "- `bound: CReal` - Approximation Error Bound, in the $L_2$ metric (with respect to the given amplitudes vector).\n",
    "- `out: QArray[QBit]` - Should of size exactly $\\log_2$(`amplitudes.len`)\n",
    "\n",
    "The `inplace_prepare_amplitudes` works the same, but for a given allocated `QArray`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "647e9afc-c2a2-4a12-9e60-566fdc4745d1",
   "metadata": {},
   "source": [
    "## Example 1: Loading Point Mass (PMF) Function\n",
    "\n",
    "This example generates a quantum program whose output state probabilities are an approximation to the PMF given.\n",
    "That is, the probability of measuring the state $|000\u27e9$ is $0.05$, $|001\u27e9$ is $0.11$,...\n",
    ", and the probability to measure $|111\u27e9$ is $0.06$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "802bc17c-14d0-40b8-a3b4-57a3d7ea6bbc",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T13:24:44.397693Z",
     "iopub.status.busy": "2024-05-07T13:24:44.397249Z",
     "iopub.status.idle": "2024-05-07T13:24:47.499053Z",
     "shell.execute_reply": "2024-05-07T13:24:47.498331Z"
    }
   },
   "outputs": [],
   "source": [
    "from classiq import Output, QArray, QBit, allocate, create_model, prepare_state, qfunc\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def main(x: Output[QArray[QBit]]):\n",
    "    probabilities = [0.05, 0.11, 0.13, 0.23, 0.27, 0.12, 0.03, 0.06]\n",
    "    prepare_state(probabilities=probabilities, bound=0.01, out=x)\n",
    "\n",
    "\n",
    "qmod = create_model(main)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "125709cb-039d-4e4e-9432-642e0a16d15f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T13:24:47.503950Z",
     "iopub.status.busy": "2024-05-07T13:24:47.502579Z",
     "iopub.status.idle": "2024-05-07T13:24:48.832811Z",
     "shell.execute_reply": "2024-05-07T13:24:48.832058Z"
    }
   },
   "outputs": [],
   "source": [
    "from classiq import synthesize, write_qmod\n",
    "\n",
    "write_qmod(qmod, \"prepare_state\")\n",
    "qprog = synthesize(qmod)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "09ad3fc6-b48d-4d0c-8eb7-df7d880ff786",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true
   },
   "source": [
    "Print the resulting probabilities:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "a881d37d-d79e-4ee2-94d4-a6fdf8008516",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T13:24:48.837870Z",
     "iopub.status.busy": "2024-05-07T13:24:48.836656Z",
     "iopub.status.idle": "2024-05-07T13:24:50.231492Z",
     "shell.execute_reply": "2024-05-07T13:24:50.230763Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Resulting probabilities: [0.045 0.104 0.134 0.227 0.289 0.112 0.034 0.055]\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "\n",
    "from classiq import execute\n",
    "\n",
    "res = execute(qprog).result()[0].value\n",
    "\n",
    "probs = np.zeros(8)\n",
    "for sample in res.parsed_counts:\n",
    "    probs[int(sample.state[\"x\"])] = sample.shots / res.num_shots\n",
    "print(\"Resulting probabilities:\", probs)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a512d075-7ae6-44be-94eb-a051fb4fd96b",
   "metadata": {},
   "source": [
    "## Example 2 - Preparating Amplitudes\n",
    "\n",
    "This example loads a normalized linear space between -1 to 1. The load\n",
    "state has an accuracy of 99 present under the L2 norm."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "ddf1d24d-1fba-41de-b8d3-d44c2de94104",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T13:24:50.236476Z",
     "iopub.status.busy": "2024-05-07T13:24:50.235343Z",
     "iopub.status.idle": "2024-05-07T13:24:50.247163Z",
     "shell.execute_reply": "2024-05-07T13:24:50.246483Z"
    }
   },
   "outputs": [],
   "source": [
    "from classiq import (\n",
    "    Output,\n",
    "    QArray,\n",
    "    QBit,\n",
    "    allocate,\n",
    "    create_model,\n",
    "    prepare_amplitudes,\n",
    "    qfunc,\n",
    ")\n",
    "from classiq.execution import ClassiqBackendPreferences, ExecutionPreferences\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def main(x: Output[QArray[QBit]]):\n",
    "    amps = np.linspace(-1, 1, 8)\n",
    "    amps = amps / np.linalg.norm(amps)\n",
    "    prepare_amplitudes(amplitudes=amps.tolist(), bound=0, out=x)\n",
    "\n",
    "\n",
    "backend_preferences = ClassiqBackendPreferences(\n",
    "    backend_name=\"aer_simulator_statevector\"\n",
    ")\n",
    "execution_preferences = ExecutionPreferences(\n",
    "    num_shots=1, backend_preferences=backend_preferences\n",
    ")\n",
    "qmod = create_model(main, execution_preferences=execution_preferences)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "d612bf06-8192-4b86-b5dd-2f09964d025a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T13:24:50.251743Z",
     "iopub.status.busy": "2024-05-07T13:24:50.250535Z",
     "iopub.status.idle": "2024-05-07T13:24:51.426596Z",
     "shell.execute_reply": "2024-05-07T13:24:51.425983Z"
    }
   },
   "outputs": [],
   "source": [
    "from classiq import synthesize, write_qmod\n",
    "\n",
    "write_qmod(qmod, \"prepare_amplitudes\", decimal_precision=15)\n",
    "qprog = synthesize(qmod)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3d18791b-4b25-4ef9-9030-7a08bf4957ed",
   "metadata": {
    "jp-MarkdownHeadingCollapsed": true
   },
   "source": [
    "Print the resulting amplitudes:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "d34eb750-f660-4231-96c8-16db5351119b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T13:24:51.430349Z",
     "iopub.status.busy": "2024-05-07T13:24:51.429881Z",
     "iopub.status.idle": "2024-05-07T13:24:52.707465Z",
     "shell.execute_reply": "2024-05-07T13:24:52.706748Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Resulting amplitudes: [ 0.54006172  0.38575837  0.23145502  0.07715167 -0.07715167 -0.23145502\n",
      " -0.38575837 -0.54006172]\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "\n",
    "from classiq import execute\n",
    "\n",
    "res = execute(qprog).result()[0].value\n",
    "\n",
    "amps = np.zeros(8, dtype=complex)\n",
    "for sample in res.parsed_state_vector:\n",
    "    amps[int(sample.state[\"x\"])] = sample.amplitude\n",
    "\n",
    "# remove global phase\n",
    "global_phase = np.angle(amps[0])\n",
    "amps = np.real(amps / np.exp(1j * global_phase))\n",
    "\n",
    "print(\"Resulting amplitudes:\", amps)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
